package com.ushine.web.component.authority.handler;

import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
import com.ushine.web.base.model.base.DataFilterMetaDataBO;
import com.ushine.web.base.model.system.DataPermissionDO;
import com.ushine.web.component.authority.config.DataFilterThreadLocal;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.statement.select.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @Author: yls
 * @Date 2022/2/9 14:05
 * @Description: 拦截器：列级查询控制器
 * @Version 1.0
 **/
@Component
@Slf4j
public class QueryDataPermissionInterceptor extends DataPermissionInterceptor {

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        if (!InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
            log.info(ms.getId());
            PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
            mpBs.sql(this.parserSingle(mpBs.sql(), ms.getId()));
        }
    }


    /**
     * 功能描述:
     * 〈 查询的对应参数 〉
     *
     * @param select 查询体
     * @param index  索引
     * @param sql    sql语句
     * @param obj    对象
     * @return : void
     * @author : yls
     * @date : 2022/02/09 22:13
     */
    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        SelectBody selectBody = select.getSelectBody();
        if (selectBody instanceof PlainSelect) {
            PlainSelect plainSelect = (PlainSelect) selectBody;
            this.processDataPermission(plainSelect);
        } else if (selectBody instanceof SetOperationList) {
            SetOperationList setOperationList = (SetOperationList) selectBody;
            List<SelectBody> selectBodyList = setOperationList.getSelects();
            selectBodyList.forEach(s -> {
                PlainSelect plainSelect = (PlainSelect) s;
                this.processDataPermission(plainSelect);
            });
        }
    }


    /**
     * 功能描述:
     * 〈 处理列级数据权限 〉
     *
     * @param plainSelect 查询列
     * @return : void
     * @author : yls
     * @date : 2022/2/9 18:06
     */
    void processDataPermission(PlainSelect plainSelect) {
        // 获取权限过滤相关的信息
        DataFilterMetaDataBO dataFilterMetaDataBO = DataFilterThreadLocal.getDataFilter();
        // 不需要权限过滤
        if (dataFilterMetaDataBO == null) {
            log.info("*****************不需要进行数据列级拦截");
            return;
        }
        DataPermissionDO dataPermissionDO = dataFilterMetaDataBO.getDataPermission();
        if (dataPermissionDO == null) {
            log.info("*******数据权限为空********不需要进行数据列级拦截");
            return;
        }
        // 列级数据权限
        String dataColumnExclude = dataPermissionDO.getDataColumnExclude();
        String dataColumnInclude = dataPermissionDO.getDataColumnInclude();
        // 组装列
        List<String> includeColumns = new ArrayList<>();
        List<String> excludeColumns = new ArrayList<>();
        // 只包含这几个字段，也就是不是这几个字段的，直接删除
        if (StringUtils.isNotEmpty(dataColumnInclude)) {
            includeColumns = Arrays.asList(dataColumnInclude.split(StringPool.COMMA));

        }
        // 需要排除这几个字段
        if (StringUtils.isNotEmpty(dataColumnExclude)) {
            excludeColumns = Arrays.asList(dataColumnExclude.split(StringPool.COMMA));
        }
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        List<SelectItem> removeItems = new ArrayList<>();
        boolean itemIsEmpty = CollectionUtils.isNotEmpty(selectItems) && (CollectionUtils.isNotEmpty(includeColumns) || CollectionUtils.isNotEmpty(excludeColumns));
        if (itemIsEmpty) {
            for (SelectItem selectItem : selectItems) {
                // 暂不处理其他类型的selectItem
                if (selectItem instanceof SelectExpressionItem) {
                    SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
                    String itemName = selectExpressionItem.toString();
                    boolean columnsIsEmpty = (CollectionUtils.isNotEmpty(includeColumns) && !includeColumns.contains(itemName))
                            || (!CollectionUtils.isEmpty(excludeColumns) && excludeColumns.contains(itemName));
                    if (columnsIsEmpty) {
                        removeItems.add(selectItem);
                    }
                } else if (selectItem instanceof AllTableColumns) {
                    removeItems.add(selectItem);
                }
            }
            if (CollectionUtils.isNotEmpty(removeItems)) {
                selectItems.removeAll(removeItems);
                plainSelect.setSelectItems(selectItems);
            }
        }
    }
}
